﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows.Forms;
using ICSharpCode.SharpZipLib.Zip;

namespace DEVotion
{
	class LOVEProject
	{
		private string _projectfile = "";
		private string _projectpath = "";
		private string _projecttemp = "";
		private string _projectsource = "";
		private TreeView _tree = null;

		public string ProjectSource { get { return _projectsource; } }
		public string ProjectFile { get { return _projectfile; } }
		public string ProjectPath { get { return _projectpath; } }
		public string TempFolder { get { return _projecttemp; } }
		public TreeView Tree { get { return _tree; } }

		public LOVEProject(string projectfile, TreeView tree)
		{
			FileInfo fi = new FileInfo(projectfile);
			if (!fi.Exists)
			{
				FileStream f = File.Create(projectfile);
				f.Close();

				fi = new FileInfo(projectfile);
				_projectsource = projectfile;
				_projectfile = fi.Name;
				_projectpath = fi.DirectoryName;
				_projecttemp = Path.GetTempPath() + "devotionnew";

				Directory.CreateDirectory(_projecttemp);
				File.WriteAllText(_projecttemp + "\\game.conf", "title = \"New Project\"\nauthor = \"Me\"\nlove_version = \"0.5.0\"");
				string s = "--AutoGenerated by DEVotion\n\nfunction load()\nend\n\nfunction update(dt)\nend\n\nfunction draw()\nend\n";

				string p = (new FileInfo(Application.ExecutablePath)).DirectoryName;
				if (File.Exists(p + "\\MainStub.txt"))
					s = File.ReadAllText(p + "\\MainStub.txt");
				File.WriteAllText(_projecttemp + "\\main.lua", s);
				Zip();

				Directory.Delete(_projecttemp, true);
			}

			_projectsource = projectfile;
			_projectfile = fi.Name;
			_projectpath = fi.DirectoryName;
			_projecttemp = Path.GetTempPath() + "devotion" + DateTime.Now.Ticks.ToString() + "\\";
			_tree = tree;
			Unzip();
		}
		~LOVEProject()
		{
			if (!String.IsNullOrEmpty(_projecttemp) && Directory.Exists(_projecttemp))
			{
				Zip();
				Directory.Delete(_projecttemp, true);
			}
		}

		public void CreateFolder(string folderName, string path)
		{
			Directory.CreateDirectory(_projecttemp + path + folderName);
		}
		public void CreateFile(string fileName, string extension, string path)
		{
			File.WriteAllText(_projecttemp + path + "\\" + fileName + extension, "");
		}
		public void RenameFolder(string oldname, string newname)
		{
			Directory.Move(oldname, newname);
		}

		/// <summary>
		/// Decompresses the current project into a temp folder
		/// </summary>
		public void Unzip()
		{
			if (!String.IsNullOrEmpty(_projecttemp) && Directory.Exists(_projecttemp))
				Directory.Delete(_projecttemp, true);

			Directory.CreateDirectory(_projecttemp);

			using (ZipInputStream s = new ZipInputStream(File.OpenRead(_projectsource)))
			{
				ZipEntry theEntry;
				while ((theEntry = s.GetNextEntry()) != null)
				{

					string fileName = Path.GetFileName(theEntry.Name);

					if (fileName != String.Empty)
					{
						string fullpath = _projecttemp + theEntry.Name;
						if (!Directory.GetParent(fullpath).Exists)
							Directory.GetParent(fullpath).Create();

						using (FileStream streamWriter = File.Create(fullpath))
						{

							int size = 2048;
							byte[] data = new byte[2048];
							while (true)
							{
								size = s.Read(data, 0, data.Length);
								if (size > 0)
								{
									streamWriter.Write(data, 0, size);
								}
								else
								{
									break;
								}
							}
						}
						FileInfo fi = new FileInfo(fullpath);
					}
				}
			}
			RenderTree();
		}

		public void RenderTree()
		{
			_tree.Nodes.Clear();
			TreeNode root = _tree.Nodes.Add("root", ProjectFile, "package", "package");

			PopulateNode(TempFolder, root);
			_tree.Sort();
			root.Expand();
		}
		public void PopulateNode(string directoryValue, TreeNode parentNode)
		{
			string[] directoryArray = Directory.GetDirectories(directoryValue);
			string subdir;
			parentNode.Nodes.Clear();
			try
			{
				if (directoryArray.Length != 0)
				{
					foreach (string directory in directoryArray)
					{
						subdir = directory.Substring(
						directory.LastIndexOf('\\') + 1,
						directory.Length - directory.LastIndexOf('\\') - 1);

						TreeNode myNode = new TreeNode(subdir);
						myNode.SelectedImageKey = myNode.ImageKey = "folder";

						parentNode.Nodes.Add(myNode);

						PopulateNode(directory, myNode);
					}
				}
				string[] files = Directory.GetFiles(directoryValue);
				foreach (string file in files)
				{
					FileInfo fi = new FileInfo(file);
					if (fi.Extension.ToLower() != ".conf")
					{
						TreeNode myNode = new TreeNode(fi.Name);
						myNode.SelectedImageKey = myNode.ImageKey = ImageKeyFromExtension(fi.Extension);
						parentNode.Nodes.Add(myNode);
					}
				}
			}
			catch (UnauthorizedAccessException)
			{
				parentNode.Nodes.Add("Access denied");
			} // end catch
		}
		public string PathFromNode(TreeNode node)
		{
			string p = node.FullPath.Replace(_tree.Nodes[0].FullPath, "");
			p = p.Trim('\\');
			return _projecttemp + p;
		}
		public string PathFromTag(object tag)
		{
			string p = tag.ToString().Replace(_tree.Nodes[0].FullPath + "\\", "");
			p = p.Trim('\\');
			return _projecttemp + p;
		}
		private string ImageKeyFromExtension(string extension)
		{
			string idx = "blank";
			switch (extension)
			{
				case ".love":
					idx = "package";
					break;
				case ".png":
				case ".gif":
				case ".jpg":
					idx = "image";
					break;
				case ".lua":
					idx = "script";
					break;
				case ".conf":
					idx = "config";
					break;
				case ".ogg":
				case ".mp3":
				case ".wav":
					idx = "sound";
					break;
				case ".xm":
				case ".mod":
					idx = "music";
					break;
				case ".txt":
					idx = "page";
					break;
			}
			return idx;
		}
		/// <summary>
		/// Compresses the current project back into the original LOVE package
		/// </summary>
		public void Zip()
		{
			string[] filenames = Directory.GetFiles(_projecttemp, "*.*", SearchOption.AllDirectories);

			// 'using' statements gaurantee the stream is closed properly which is a big source
			// of problems otherwise.  Its exception safe as well which is great.
			try
			{
				using (ZipOutputStream s = new ZipOutputStream(File.Create(_projectsource)))
				{

					s.SetLevel(9); // 0 - store only to 9 - means best compression
					s.UseZip64 = UseZip64.Off;

					byte[] buffer = new byte[4096];

					foreach (string file in filenames)
					{

						// Using GetFileName makes the result compatible with XP
						// as the resulting path is not absolute.
						string f = file.Replace(_projecttemp, "").Replace("\\", "/");
						ZipEntry entry = new ZipEntry(f);

						// Setup the entry data as required.

						// Crc and size are handled by the library for seakable streams
						// so no need to do them here.

						// Could also use the last write time or similar for the file.
						entry.DateTime = DateTime.Now;
						s.PutNextEntry(entry);

						using (FileStream fs = File.OpenRead(file))
						{

							// Using a fixed size buffer here makes no noticeable difference for output
							// but keeps a lid on memory usage.
							int sourceBytes;
							do
							{
								sourceBytes = fs.Read(buffer, 0, buffer.Length);
								s.Write(buffer, 0, sourceBytes);
							} while (sourceBytes > 0);
						}
					}

					// Finish/Close arent needed strictly as the using statement does this automatically

					// Finish is important to ensure trailing information for a Zip file is appended.  Without this
					// the created file would be invalid.
					s.Finish();

					// Close is important to wrap things up and unlock the file.
					s.Close();
				}
			}
			catch (Exception ex)
			{
				if (MessageBox.Show("Error repackaging project:\n\n" + ex.Message, "Repackaging Error", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error) == DialogResult.Retry) Zip();
			}
		}
	}
}
